import argparse
import string
import sys

class Stack:
    def __init__(self):
        self.value = []
    def push(self, reg):
        self.value.insert(0, reg.get_value())
    def pop(self):
        if self.value != []:
            return self.value.pop(0)
        else:
            raise IndexError
    def get_string(self):
        res = ''
        for item in self.value:
            for elem in item:
                res += str(elem)
            res += '\n'
        return res[:-1]

class Registry:
    def __init__(self, capacity = 32):
        self.capacity = capacity
        self.value = []
        for bit in range(capacity):
            self.value.append(0)

    def get_value(self):
        return self.value

    def get_8low(self):
        return self.value[24:]

    def get_8hight(self):
        return self.value[16:24]

    def get_16(self):
        return self.value[16:]

    def set_8low(self, value):
        item = 24
        for digit in value:
            self.value[item] = digit
            item = item + 1

    def set_8hight(self, value):
        item = 16
        for digit in value:
            self.value[item] = digit
            item = item + 1

    def set_16(self, value):
        item = 16
        for digit in value:
            self.value[item] = digit
            item = item + 1

    def get_string(self):
        res = ''
        for bit in self.value:
            res += str(bit)
        return res
    
    def set_value(self, value):
        if self.capacity == len(value):
            self.value = value
        elif len(value) == 16:
            for digit in range(16,32):
                self.value[digit] = value[digit - 16]
        elif len(value) == 8:
            for digit in range(24,32):
                self.value[digit] = value[digit - 24]
        else:
            raise ValueError
        
    def set_flag(self, flag, value):
        if flag == 'C':
            self.value[0] = value
    
    def get_flag(self, flag):
        if flag == 'C':
            return self.value[0]
        
class Vm:
    def __init__(self):
        self.flags = Registry()
        self.eax = Registry()
        self.edx = Registry()
        self.ecx = Registry()
        self.ebx = Registry()
        self.flags = Registry()
        self.stack = Stack()
        
    def execute_command(self, command):
        if command == 'exit':
            import sys
            sys.exit()
        elif command == 'flags':
            print self.flags.get_string()
        elif command == 'eax':
            print self.eax.get_string()
        elif command == 'ebx':
            print self.ebx.get_string()
        elif command == 'edx':
            print self.edx.get_string()
        elif command == 'ecx':
            print self.ecx.get_string()
        elif command == 'ax':
            print self.eax.get_string()[16:]
        elif command == 'bx':
            print self.ebx.get_string()[16:]
        elif command == 'dx':
            print self.edx.get_string()[16:]
        elif command == 'cx':
            print self.ecx.get_string()[16:]
        elif command == 'al':
            print self.eax.get_string()[24:]
        elif command == 'bl':
            print self.ebx.get_string()[24:]
        elif command == 'dl':
            print self.edx.get_string()[24:]
        elif command == 'cl':
            print self.ecx.get_string()[24:]
        elif command == 'ah':
            print self.eax.get_string()[16:24]
        elif command == 'bh':
            print self.ebx.get_string()[16:24]
        elif command == 'dh':
            print self.edx.get_string()[16:24]
        elif command == 'ch':
            print self.ecx.get_string()[16:24]
        elif command == 'stack':
            print self.stack.get_string()
        else:
            command_new_frm = string.split(command, ' ')
            if len(command_new_frm) == 1:
                if command_new_frm[0] == 'pushf':
                    self.pushf_command()
                elif command_new_frm[0] == 'popf':
                    self.popf_command()
                elif command_new_frm[0] == 'pusha':
                    self.pusha_command()
                elif command_new_frm[0] == 'popa':
                    self.popa_command()
            elif len(command_new_frm) == 2:
                if command_new_frm[0] == 'push':
                    self.push_command(command_new_frm[1])
                elif command_new_frm[0] == 'pop':
                    self.pop_command(command_new_frm[1])
            elif len(command_new_frm) == 3:
                if command_new_frm[0] == 'mov':
                    self.mov_command(command_new_frm[1][:-1], command_new_frm[2])

    def push_command(self, reg):
        if reg == 'eax':
            self.stack.push(self.eax)
        elif reg == 'ebx':
            self.stack.push(self.ebx)
        elif reg == 'edx':
            self.stack.push(self.edx)
        elif reg == 'ecx':
            self.stack.push(self.ecx)
        else:
            raise ValueError
    
    def pop_command(self, reg):
        if reg == 'eax':
            self.eax.set_value(self.stack.pop())
        elif reg == 'ebx':
            self.ebx.set_value(self.stack.pop())
        elif reg == 'edx':
            self.edx.set_value(self.stack.pop())
        elif reg == 'ecx':
            self.ecx.set_value(self.stack.pop())
        else:
            raise ValueError
    def pusha_command(self):
        self.stack.push(self.eax)
        self.stack.push(self.ebx)
        self.stack.push(self.edx)
        self.stack.push(self.ecx)
    def popa_command(self):
        self.ecx.set_value(self.stack.pop())
        self.edx.set_value(self.stack.pop())
        self.ebx.set_value(self.stack.pop())
        self.eax.set_value(self.stack.pop())
    def pushf_command(self):
        self.stack.push(self.flags)
    def popf_command(self):
        self.flags.set_value(self.stack.pop())

    def mov_command(self, reg1, reg2):
        #import pdb; pdb.set_trace()
        if reg1[0] == 'e' and reg2[0] == 'e':
            eval('self.' + reg1).set_value(eval('self.'+reg2).get_value())
        elif reg1[-1] == 'x' and reg2[-1] == 'x':
            eval('self.e' + reg1[0] + 'x').set_16(eval('self.e'+reg2[0] + 'x').get_16())
        elif reg1[-1] == 'l' and reg2[-1] == 'l':
            eval('self.e' + reg1[0] + 'x').set_8low(eval('self.e'+reg2[0]+'x').get_8low())
        elif reg1[-1] == 'h' and reg2[-1] == 'h':
            eval('self.e' + reg1[0] + 'x').set_8hight(eval('self.e'+reg2[0]+'x').get_8hight())
        elif reg1[-1] == 'l' and reg2[-1] == 'h':
            eval('self.e' + reg1[0] + 'x').set_8low(eval('self.e'+reg2[0]+'x').get_8hight())
        elif reg1[-1] == 'h' and reg2[-1] == 'l':
            val = eval('self.e'+reg2[0]+'x').get_8low()
            eval('self.e' + reg1[0] + 'x').set_8hight(val)
        else:
            if reg2[:2] == '0x':
                if reg1[0] == 'e' or reg1 == 'flags':
                    capacity = 32
                elif reg1[-1] == 'x':
                    capacity = 16
                elif reg1[-1] == 'l' or reg1[-1] == 'h':
                    capacity = 8
                
                s = bin(int(reg2, 16))[2:]
                length = len(s)
                diff = capacity - length
                if diff >= 0:
                    num = []
                    for digit in range(diff):
                        num.append(0)
                    for digit in range(length):
                        num.append(int(s[digit]))
                    eval('self.' + reg1).set_value(num)
                else:
                    raise ValueError
                    
            else:
                raise ValueError
       
    def execute_file(self, file_name):
        #import pdb; pdb.set_trace()
        fin = open(file_name)
        for line in fin:
            print '>>> ', line
            self.execute_command(line)
        self.interactive_mode()

    def interactive_mode(self):
        invit = '>>> '
        input_command = ''
        while True:
            input_command = str(raw_input(invit))
            self.execute_command(input_command)

def main():
    DESCRIPTION = 'Pentium III emulator with commands: mov, push, pop, pusha, popa, pushf, popf'
    parser = argparse.ArgumentParser(description = DESCRIPTION)
    parser.add_argument('--command', help = 'Execute single command')
    parser.add_argument('--file', help = 'Execute file with instructions for Pentium III')
       
    args = parser.parse_args()

    vmi = Vm()

    if args.command:
        vmi.execute_command(args.command)
    elif args.file != None:
        vmi.execute_file(args.file)
    else:
        vmi.interactive_mode()
        
if __name__=="__main__":
    main()
